package cn.xinfei.xdecision.manager.web.handler;

import cn.xinfei.xdecision.common.model.common.ResponseEntityBuilder;
import cn.xinfei.xdecision.common.model.common.ResponseEntityDto;
import cn.xinfei.xdecision.common.utils.exception.ApiException;
import cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import static cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum.PARAMS_EXCEPTION;

/**
 * Created by maying on 2023/03/20.
 **/
@RestControllerAdvice
@AllArgsConstructor
@Slf4j
public class GlobalExceptionHandler {
	/**
	 * 处理 SpringMVC 请求参数缺失
	 *
	 * 例如说，接口上设置了 @RequestParam("xx") 参数，结果并未传递 xx 参数
	 */
	@ExceptionHandler(value = MissingServletRequestParameterException.class)
	public ResponseEntityDto<T> missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException ex) {
		log.warn("[missingServletRequestParameterExceptionHandler]", ex);
		return ResponseEntityBuilder.buildErrorResponse(PARAMS_EXCEPTION.getCode(), String.format("请求参数缺失:%s", ex.getParameterName()));
	}

	/**
	 * 处理 SpringMVC 参数校验不正确
	 */
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public ResponseEntityDto<T> methodArgumentNotValidExceptionExceptionHandler(MethodArgumentNotValidException ex) {
		log.warn("[methodArgumentNotValidExceptionExceptionHandler]", ex);
		FieldError fieldError = ex.getBindingResult().getFieldError();
		assert fieldError != null; // 断言，避免告警
		return ResponseEntityBuilder.buildErrorResponse(PARAMS_EXCEPTION.getCode(), String.format("请求参数不正确:%s", fieldError.getDefaultMessage()));
	}

	/**
	 * 处理 SpringMVC 参数绑定不正确，本质上也是通过 Validator 校验
	 */
	@ExceptionHandler(BindException.class)
	public ResponseEntityDto<T> bindExceptionHandler(BindException ex) {
		log.warn("[handleBindException]", ex);
		FieldError fieldError = ex.getFieldError();
		assert fieldError != null; // 断言，避免告警
		return ResponseEntityBuilder.buildErrorResponse(PARAMS_EXCEPTION.getCode(), String.format("请求参数不正确:%s", fieldError.getDefaultMessage()));
	}

	/**
	 * 处理 Validator 校验不通过产生的异常
	 */
	@ExceptionHandler(value = ConstraintViolationException.class)
	public ResponseEntityDto<T> constraintViolationExceptionHandler(ConstraintViolationException ex) {
		log.warn("[constraintViolationExceptionHandler]", ex);
		ConstraintViolation<?> constraintViolation = ex.getConstraintViolations().iterator().next();
		return ResponseEntityBuilder.buildErrorResponse(PARAMS_EXCEPTION.getCode(), String.format("请求参数不正确:%s", constraintViolation.getMessage()));
	}

	/**
	 * 处理 SpringMVC 请求方法不正确
	 *
	 * 例如说，A 接口的方法为 GET 方式，结果请求方法为 POST 方式，导致不匹配
	 */
	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
	public ResponseEntityDto<T> httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException ex) {
		log.warn("[httpRequestMethodNotSupportedExceptionHandler]", ex);
		return ResponseEntityBuilder.buildErrorResponse(PARAMS_EXCEPTION.getCode(), String.format("请求方法不正确:%s", ex.getMessage()));
	}

	/**
	 * 处理业务异常 ApiException
	 */
	@ExceptionHandler(ApiException.class)
	public ResponseEntityDto<T> apiExceptionHandler(ApiException ae) {
		log.warn("[apiExceptionHandler]", ae);
		return ResponseEntityBuilder.buildErrorResponse(ae.errCode, ae.message);
	}

	@ExceptionHandler(NullPointerException.class)
	public ResponseEntityDto<T> nullPointerExceptionHandler(NullPointerException npe) {
		log.warn("[nullPointerExceptionHandler]", npe);
		return ResponseEntityBuilder.buildErrorResponse(ErrorCodeEnum.NULL_POINT_EREXCEPTION.getCode(), ErrorCodeEnum.NULL_POINT_EREXCEPTION.getMessage());
	}

	@ExceptionHandler(ClassCastException.class)
	public ResponseEntityDto<T> classCastExceptionHandler(ClassCastException ce) {
		log.warn("[classCastExceptionHandler]", ce);
		return ResponseEntityBuilder.buildErrorResponse(ErrorCodeEnum.CLASS_CAST_EXCEPTION.getCode(), ErrorCodeEnum.CLASS_CAST_EXCEPTION.getMessage());
	}

	@ExceptionHandler(Exception.class)
	public ResponseEntityDto<T> exceptionHandler(Exception e) {
		log.warn("[exceptionHandler]", e);
		return ResponseEntityBuilder.buildErrorResponse(ErrorCodeEnum.SERVER_ERROR.getCode(), ErrorCodeEnum.SERVER_ERROR.getMessage());
	}


}
